use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::path::PathBuf;

use async_trait::async_trait;

use zen_engine::loader::{LoaderError, LoaderResult};
use zen_engine::model::DecisionContent;

use crate::loader::RulesLoaderExt;

/// Loads decisions based on filesystem root

/// 系统加载器，从文件系统加载规则
#[derive(Debug)]
pub struct SystemLoader {
    pub root: String,
}

#[async_trait]
impl RulesLoaderExt for SystemLoader {
    async fn load_rule(
        &self,
        key: &str,
    ) -> LoaderResult<DecisionContent> {
        self.read_from_file(key).await
    }
}
impl SystemLoader {
    pub fn new(root: String) -> Self {
        Self { root }
    }
    fn key_to_path<K: AsRef<str>>(
        &self,
        key: K,
    ) -> PathBuf {
        Path::new(&self.root).join(key.as_ref())
    }

    async fn read_from_file<K>(
        &self,
        key: K,
    ) -> LoaderResult<DecisionContent>
    where
        K: AsRef<str>,
    {
        let path = self.key_to_path(key.as_ref());
        if !Path::exists(&path) {
            return Err(LoaderError::NotFound(String::from(key.as_ref())).into());
        }

        let file = File::open(path).map_err(|e| LoaderError::Internal { key: String::from(key.as_ref()), source: e.into() })?;

        let reader = BufReader::new(file);
        let result: DecisionContent =
            serde_json::from_reader(reader).map_err(|e| LoaderError::Internal { key: String::from(key.as_ref()), source: e.into() })?;
        Ok(result)
    }
}
